﻿using System;
using System.Collections.Generic;
using IBNet.UtilsDataStructuresAndExtensions;

namespace IBNet.DayTrade.Indicators
{
   public class SimpleMovingAverage : Indicator
   {
      private readonly Func<FixedLengthCollection<PriceBar>, decimal> _smaIndicator;
      private readonly int _smaPeriod;
      private readonly PriceBarType _barType;

      public SimpleMovingAverage(int smaPeriod, PriceBarType barType) 
         : this(smaPeriod, barType, "SimpleMovingAverage")
      {
      }

      public SimpleMovingAverage(int smaPeriod, PriceBarType barType, string name)
         : base(name)
      {
         if (smaPeriod < 2)
            throw new ArgumentException("smaPeriod must be greater than or equal to 2.");
         if (String.IsNullOrEmpty(name))
            throw new ArgumentNullException("name");

         _smaPeriod = smaPeriod;
         _barType = barType;

         switch (barType)
         {
            case PriceBarType.Open:
            {
               _smaIndicator = (FixedLengthCollection<PriceBar> priceBars) =>
               {
                  decimal sum_of_opens_across_period = 0;

                  for (int i = 0; i != smaPeriod; ++i)
                  {
                     sum_of_opens_across_period += priceBars[i].Open;
                  }

                  decimal smaValue = sum_of_opens_across_period / smaPeriod;
                  return smaValue;
               };
               break;
            }
            case PriceBarType.High:
            {
               _smaIndicator = (FixedLengthCollection<PriceBar> priceBars) =>
               {
                  decimal sum_of_highs_across_period = 0;

                  for (int i = 0; i != smaPeriod; ++i)
                  {
                     sum_of_highs_across_period += priceBars[i].High;
                  }

                  decimal smaValue = sum_of_highs_across_period / smaPeriod;
                  return smaValue;
               };
               break;
            }
            case PriceBarType.Low:
            {
               _smaIndicator = (FixedLengthCollection<PriceBar> priceBars) =>
               {
                  decimal sum_of_lows_across_period = 0;

                  for (int i = 0; i != smaPeriod; ++i)
                  {
                     sum_of_lows_across_period += priceBars[i].Low;
                  }

                  decimal smaValue = sum_of_lows_across_period / smaPeriod;
                  return smaValue;
               };
               break;
            }
            case PriceBarType.Close:
            {
               _smaIndicator = (FixedLengthCollection<PriceBar> priceBars) =>
               {
                  decimal sum_of_closes_across_period = 0;

                  for (int i = 0; i != smaPeriod; ++i)
                  {
                     sum_of_closes_across_period += priceBars[i].Close;
                  }

                  decimal smaValue = sum_of_closes_across_period / smaPeriod;
                  return smaValue;
               };
               break;
            }
            case PriceBarType.Volume:
            {
               _smaIndicator = (FixedLengthCollection<PriceBar> priceBars) =>
               {
                  decimal sum_of_volume_across_period = 0;

                  for (int i = 0; i != smaPeriod; ++i)
                  {
                     sum_of_volume_across_period += priceBars[i].Volume;
                  }

                  decimal smaValue = sum_of_volume_across_period / smaPeriod;
                  return smaValue;
               };
               break;
            }
         }
      }

      public override decimal? CalculateIndicator(FixedLengthCollection<PriceBar> priceBars)
      {
         if (_smaPeriod > priceBars.Count)
            return null;

         decimal? smaValue = _smaIndicator(priceBars);
         return smaValue;
      }
   }
}
